/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cflp;

import java.util.LinkedList;

/**
 *
 * @author gbula
 */
public class BusquedaTabu {

    private int ll, cp, eb;
    private Solucion solucionOp;
    private Network net;
    private Individuo ind;

    public BusquedaTabu(int l, int p, int e, Network n) {
        ll = l;
        cp=p;
        eb=e;
        net = n;
        ind = new Individuo(net);
        ejecutarAlgoritmo();
        solucionOp.imprimir();
    }

    public void ejecutarAlgoritmo() {
        LinkedList<Movimiento> listaTabu = new LinkedList<Movimiento>();
        Solucion SolucionInicial = generarSolucion();
        int[] individuoBusqueda = SolucionInicial.getIndividuo();
        int[] mejorIndividuo = new int[individuoBusqueda.length];
        System.arraycopy(individuoBusqueda, 0, mejorIndividuo, 0, individuoBusqueda.length);
        int mejorSolucion = SolucionInicial.getFuncion();
        //System.out.println(mejorSolucion + "--");
         Movimiento puntoBusqueda = new Movimiento(-1, -1, 0, 'n');
        for (int l = 0; l < cp; ++l) {
            LinkedList<Movimiento> mov = buscarMejoresVecinos(individuoBusqueda);
            Movimiento moviIteracion;
            while (true) {
                int minCost = mov.getFirst().getFuncion();
                moviIteracion = mov.remove();
                if (minCost < mejorSolucion) {
                    mejorSolucion = minCost;
                    puntoBusqueda = moviIteracion;
                    System.arraycopy(individuoBusqueda, 0, mejorIndividuo, 0, individuoBusqueda.length);
                    switch (puntoBusqueda.getMov()) {
                        case ('c'): {
                           mejorIndividuo[puntoBusqueda.getPos1()] = 0;
                           mejorIndividuo[puntoBusqueda.getPos2()] = 1;
                            break;
                        }
                        case ('a'): {
                           mejorIndividuo[puntoBusqueda.getPos1()] = 1;
                            break;
                        }
                        default: {
                            mejorIndividuo[puntoBusqueda.getPos1()] = 0;
                            break;
                        }
                    }
                    agregarLista(listaTabu, moviIteracion);
                    break;
                } else {
                    if (!estaMovimiento(listaTabu, moviIteracion)) {
                        puntoBusqueda = moviIteracion;
                        agregarLista(listaTabu, moviIteracion);
                        break;
                    }
                }
            }
            switch (puntoBusqueda.getMov()) {
                case ('c'): {
                    individuoBusqueda[puntoBusqueda.getPos1()] = 0;
                    individuoBusqueda[puntoBusqueda.getPos2()] = 1;
                    break;
                }
                case ('a'): {
                    individuoBusqueda[puntoBusqueda.getPos1()] = 1;
                    break;
                }
                default: {
                    individuoBusqueda[puntoBusqueda.getPos1()] = 0;
                    break;
                }

            }
            solucionOp = new Solucion(mejorIndividuo, mejorSolucion);
        }
    }

    void imprimirLista(LinkedList<Movimiento> listaTabu) {
        System.out.print("(");
        for (Movimiento m : listaTabu) {
            System.out.print(" " + m.getPos1() + m.getMov() + m.getPos2() + " ");
        }
        System.out.println(")");
    }

    private void agregarLista(LinkedList<Movimiento> listaTabu, Movimiento moviIteracion) {
        if (listaTabu.size() == ll) {
            listaTabu.remove();
        }
        listaTabu.add(moviIteracion);
    }

    private boolean estaMovimiento(LinkedList<Movimiento> listaTabu, Movimiento moviIteracion) {
        for (Movimiento m : listaTabu) {
            if ((m.getPos1() == moviIteracion.getPos1() && m.getPos2() == moviIteracion.getPos2()) || (m.getPos2() == moviIteracion.getPos1() && m.getPos1() == moviIteracion.getPos2())) {
                return true;
            }
        }
        return false;
    }

    public Solucion generarSolucion() {
        double p = 0.5;
        int n = net.getNodesNumber();
        int[] w = new int[n];
        for (int i = 0; i < n; ++i) {
            w[i] = (Math.random() < p) ? 1 : 0;
        }
        int sumaw = 0;
        java.util.Vector v = new java.util.Vector();
        int j = 0;
        for (int i = 0; i < w.length; ++i) {
            if (w[i] == 1) {
                sumaw = sumaw + net.getWarehouseCapacity()[i];
            } else {
                v.add(j, new Integer(i));
                j = j + 1;
            }
        }
        while (sumaw < net.getDemTotal()) {
            int a = (int) Math.floor(Math.random() * v.size());
            int pos = Integer.parseInt(v.get(a).toString());
            w[pos] = 1;
            sumaw = sumaw + net.getWarehouseCapacity()[pos];
            v.remove(a);
        }
        int f = ind.totalCost(w);
        return new Solucion(w, f);
    }

    public LinkedList<Movimiento> buscarMejoresVecinos(int[] in) {
        LinkedList<Movimiento> movL = new LinkedList<Movimiento>();
        int[] wr = new int[in.length];
        System.arraycopy(in, 0, wr, 0, wr.length);
        java.util.Vector ones = new java.util.Vector();
        java.util.Vector zeroes = new java.util.Vector();
        for (int i = 0; i < wr.length; ++i) {
            if (wr[i] == 1) {
                ones.add(new Integer(i));
            } else {
                zeroes.add(new Integer(i));
            }
        }
        //c es cambiar posicion
        //a adicionar bodega
        //r retirar bogega
        //Movimientos de adicionra bodegas
        for (int j = 0; j < zeroes.size(); ++j) {
            int posA = Integer.parseInt(zeroes.get(j).toString());
            int[] wrAux = new int[in.length];
            System.arraycopy(wr, 0, wrAux, 0, wr.length);
            wrAux[posA] = 1;
            int fAux = ind.totalCost(wrAux);
            Movimiento movAux = new Movimiento(posA, -1, fAux, 'a');
            agregarOrdenado(movL, movAux);
        }
        //Movimientos retirando bodegas
        if ((ones.size() - 1) * net.getCapacity() > net.getDemTotal()) {
            for (int j = 0; j < ones.size(); ++j) {
                int posR = Integer.parseInt(ones.get(j).toString());
                int[] wrAux = new int[in.length];
                System.arraycopy(wr, 0, wrAux, 0, wr.length);
                wrAux[posR] = 0;
                int fAux = ind.totalCost(wrAux);
                Movimiento movAux = new Movimiento(posR, -1, fAux, 'r');
                agregarOrdenado(movL, movAux);
            }
        }

        //Movimientos de cambio de posición
        java.util.Vector mov = new java.util.Vector();
        for (int i = 0; i < ones.size(); ++i) {
            for (int j = 0; j < zeroes.size(); ++j) {
                Movimiento a = new Movimiento(Integer.parseInt(ones.get(i).toString()), Integer.parseInt(zeroes.get(j).toString()), 0, 'c');
                mov.add(a);
            }
        }

        for (int k = 1; k <= wr.length*eb; ++k) {
            int pos = (int) Math.floor(mov.size() * Math.random());
            Movimiento a = (Movimiento) mov.get(pos);
            int posU = a.getPos1();
            int posC = a.getPos2();
            int[] wrAux = new int[in.length];
            System.arraycopy(wr, 0, wrAux, 0, wr.length);
            wrAux[posU] = 0;
            wrAux[posC] = 1;
            int fAux = ind.totalCost(wrAux);
            Movimiento movAux = new Movimiento(posU, posC, fAux, 'c');
            agregarOrdenado(movL, movAux);
        }
        return movL;
    }

    private void agregarOrdenado(LinkedList<Movimiento> lista, Movimiento m) {
        boolean add = false;
        if (lista.size() == 0) {
            lista.add(m);
        } else {
            for (int i = 0; i < lista.size(); i++) {
                if (m.getFuncion() < lista.get(i).getFuncion()) {
                    lista.add(i, m);
                    add = true;
                    break;
                }
            }
            if (!add) {
                lista.add(m);
            }
        }
    }

    public Solucion getSolucionOp() {
        return solucionOp;
    }
}
